﻿from typing import List, Dict
from typedef import *
from memory import GlobalMemory
from dataclasses import dataclass, field
from enum import Enum
import numpy as np
from time import sleep
from threading import Thread
from numpy import ndarray, uint8, uint16, uint32, uint64, float32, float64
from abc import abstractmethod
from sys import stdin, stdout


class IOSTATUS(Enum):
    IDLE = 0
    READY = 1
    BUSY = 2
    DONE = 4
    ERROR = 127


class SerialInput:
    def __init__(self):
        self.__buffer: List[str] = []
        self.__status: IOSTATUS = IOSTATUS.IDLE
        self.__runner: Thread = Thread(target=self.__run, daemon=True)

    def __run(self):
        self.__buffer.append(stdin.read(1))

    def status(self, msg: IO, direction: bool) -> IO:
        if direction == True:  # read
            return IO(msg.address, 2, self.__status.value, True)
        else:
            self.__status = IOSTATUS(msg.data)
            if self.__status == IOSTATUS.READY:
                while self.__runner.is_alive():
                    sleep(1e-4)
                self.__runner.start()
                self.__status = IOSTATUS.BUSY
            return IO(msg.address, 2, 0, True)

    def read(self, msg: IO, direction: bool) -> IO:
        if direction == False:
            self.__status = IOSTATUS.ERROR
            return IO(msg.address, -1, 0, True)
        while self.__buffer == []:
            sleep(1e-4)
        return IO(msg.address, 1, self.__buffer.pop(0), True)


class SerialOutput:
    def __init__(self):
        self.__status: IOSTATUS = IOSTATUS.IDLE
        self.__runner: Thread = Thread(target=self.__run, daemon=True)
        self.__buffer: List[str] = []

    def __run(self):
        while self.__buffer != []:
            stdout.write(self.__buffer.pop(0))

    def status(self, msg: IO, direction: bool) -> IO:
        if direction == True:  # read
            return IO(msg.address, msg.size, self.__status.value, True)
        else:
            self.__status = IOSTATUS(msg.data)
            if self.__status == IOSTATUS.READY:
                if self.__runner is None:
                    self.__runner = Thread(target=self.__run, daemon=True)
                while self.__runner.is_alive():
                    sleep(1e-4)
                self.__runner.start()
                self.__status = IOSTATUS.BUSY
            return IO(msg.address, msg.size, 0, True)

    def write(self, msg: IO, direction: bool) -> IO:
        if direction == True:
            self.__status = IOSTATUS.ERROR
            return IO(msg.address, -1, 0, True)
        self.__buffer[self.__end & 0x3f] = msg.data
        self.__end += 1


class DiskIO:
    def __init__(self, filename: str):
        self.__file = open(filename, 'rb')
        self.__status: IOSTATUS = IOSTATUS.IDLE
        self.__reader: Thread = Thread(target=self.__read, daemon=True)
        self.__writer: Thread = Thread(target=self.__write, daemon=True)
        self.__buffer: List[ndarray] = [np.zeros(shape=4096, dtype=uint8)]
        self.__size: int = 0

    def __read(self):
        while self.__size > 0:
            self.__buffer.append(self.__file.read(4096))
            self.__size -= 4096
        self.__size = 0

    def __write(self):
        while self.__size > 0:
            self.__file.write(self.__buffer.pop(0))
            self.__size -= 4096
        self.__size = 0

    def seek(self, msg: IO, direction: bool) -> IO:
        if direction == True:
            return IO(msg.address, msg.size, self.__file.tell(), True)
        else:
            self.__file.seek(msg.data)
            return IO(msg.address, msg.size, 0, True)

    def status(self, msg: IO, direction: bool) -> IO:
        if direction == True:
            return IO(msg.address, msg.size, self.__status.value, True)
        else:
            self.__status = IOSTATUS(msg.data)
            if self.__status == IOSTATUS.READY:
                while self.__reader.is_alive() or self.__writer.is_alive():
                    sleep(1e-4)
                if direction == True:  # read
                    self.__reader.start()
                elif direction == False:  # write
                    self.__writer.start()
                else:#逻辑学不存在了，计算机学也不存在了
                    raise Exception("Something more serious is wrong with the universe")
            return IO(msg.address, msg.size, 0, True)


class IODevice:
    def __init__(self):
        self.__ports: Dict[int, function] = {}
        self.__size: Dict[int, int] = {}

    def __setitem__(self, port: int, object: function, length: int):
        self.__ports[port] = object
        self.__size[port] = length

    def __delitem__(self, port: int):
        del self.__ports[port]
        del self.__size[port]

    def __getitem__(self, port: int):
        return self.__ports[port]

    def __call__(self, msg: IO, direction: bool) -> IO:
        # direction: True:read, False:write
        if msg.address not in self.__ports:
            raise Exception(f"IO port {msg.address} not found")
        if msg.size > self.__size[msg.address]:
            raise Exception(f"IO port {msg.address} size mismatch")
        return self.__ports[msg.address](msg, direction)
